/*
This license applies to all parts of v8r that are not externally
maintained libraries.  The externally maintained libraries used by v8r
are:

  - v8; see LICENSE.v8 for details
  - PHP.JS; see LICENSE.phpjs for details

Copyright 2008, Infophobia Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.
    * Neither the name of Infophobia Inc. nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* -- Name: gblContext
   -- Description: This is the global context variable, used to share and distinguish the results of V8R 
                   JavaScript executions.
*/
v8::Handle<v8::Context> gblContext;

/* -- Name: ExternalGetPid()
   -- Description: Gets and returns the current process id as known to getpid()
*/
v8::Handle<v8::Value> ExternalGetPid(const v8::Arguments& Args) {
  v8::Context::Scope context_scope(gblContext);
  v8::HandleScope handle_scope;
  return v8::Integer::New(getpid());
};

/* -- Name: ExternalGetUid()
   -- Description: Gets and returns the current user id as known to getuid() : also known as the real user id
*/
v8::Handle<v8::Value> ExternalGetUid(const v8::Arguments& Args) {
  v8::Context::Scope context_scope(gblContext);
  v8::HandleScope handle_scope;
  return v8::Integer::New( getuid() );
};

/* -- Name: ExternalUse(Url, flag1, flag2, flag3...)
   -- Description: Reads, compiles and executes code at the other end of the specified url prior to the target 
                   source. Uses flags to direct behavior.
   -- Remarks: Url arguments with no specified protocol are assumed reachable via file:// 
              (for example 'my.v8rl' would become 'file://my.v8rl')
   -- Valid flag(s): 
          "::required" -- forces the entire program to terminate if this Use fails
          "::once"     -- forces V8R read, compile and execute this source code only once
*/
v8::Handle<v8::Value> ExternalUse(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);
  v8::HandleScope handle_scope;
  v8::String::AsciiValue url(args[0]); 

  // basic safety
  std::string strUrl = *url;
  if ( strUrl.size() == 0 ) return v8::Undefined(); 

  // initialize conditions
  std::string strData  = ""; 
  std::string uType = "";   
  std::string strFilename = "";
  bool useRequired = false;  
  bool useOnce = false;     
  // interrogate the non-url arguments, and set behavior flags
  for (int i = 1; i < args.Length(); i++) {
    v8::String::AsciiValue arg(args[i]);
    std::string strArg = *arg;
    if (strArg=="::required")  useRequired = true;
    if (strArg=="::once")      useOnce = true;
  } 
  // determine usage type/where should we look: fs(filesystem on this), web(a computer elsewhere in the world) 
  if ( strUrl.find("file://",0) == 0 ) { uType = "fs"; }
  else if ( strUrl.find("http://",0) == 0 ) { uType = "web"; }
  else { strUrl = "file://" + strUrl; uType = "fs"; }
  // if useOnce behavior is in effect, and the file is marked already, scream.  otherwise, mark it.
  if ( MarkedExternallyUsed(strUrl) == true ) { 
    fprintf(stderr, "WARNING: Use() failed -- respecting call Use('%s','::once')\n", strUrl.c_str() );
  } else { 
    if ( useOnce ) MarkExternallyUsed(strUrl);
  }
  // if this is a filesystem Use()... 
  if ( uType == "fs" ) {
    // strip the file:// off
    strFilename = strUrl.substr(7,strUrl.size()-7);
    // check to see if the file is prefixed with a path
    if ( strFilename.find("./",7) == std::string::npos && 
         strFilename.find("/",7) == std::string::npos &&    
         strFilename.find(":\\",8) == std::string::npos ) {
      // if not, see if the file is in the Core
      strData = ReadFile( FindV8RCoreLibrary(strFilename) );
    } else { 
      // otherwise, Use() it as it is
      char* c = new char[ strFilename.size() ];
      strcpy(c, strFilename.c_str() );
      strData = ReadFile ( c );
    }
  // if this is a web Use()
  } else if (uType == "web") { 
    // TODO: find/research the most light-weight, most robust HttpRequest-style object
  }
  if ( strData.size() > 0 ) { 
    // if from all this we have source code, pass it along to be compiled and executed
    ExecuteString(strData, strFilename, gblContext);       
  } else { 
    // if this use was required and there was no source code, scream.
    if ( useRequired ) { 
       fprintf(stderr, "FATAL: Use() failed -- respecting calling Use('%s','::required')\n", strUrl.c_str() );
        exit(-1);
    }  
  }
  return v8::Undefined();
};

/* -- Name: ExternalEvalNew(strCode)
   -- Description: Evaluates JavaScript source code in a minimal V8 context
   -- strCode: Valid JavaScript source code
*/
v8::Handle<v8::Value> ExternalEvalNew(const v8::Arguments& args) {
  v8::HandleScope handle_scope;
  v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
  v8::Handle<v8::Context> x = v8::Context::New(NULL, local);
  v8::Context::Scope context_scope(x);
  v8::String::AsciiValue src(args[0]);
  ExecuteString(*src, " ", x);
  return v8::Undefined();
};

/* -- Name: ExternalEvalCurrent(strCode)
   -- Description: Evaluates JavaScript source code in the current V8 context; anything already defined is
                   available (functions, objects, etc)
   -- strCode: Valid JavaScript source code
*/
v8::Handle<v8::Value> ExternalEvalCurrent(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);
  v8::HandleScope handle_scope;
  v8::String::AsciiValue src(args[0]);
  ExecuteString(*src, " ", gblContext);
  return v8::Undefined();
};

/* -- Name: ExternalPutEnv(Key,Value)
   -- Description: Puts Key's Value into the System Environment
*/
v8::Handle<v8::Value> ExternalPutEnv(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  v8::String::AsciiValue env_key(args[0]);
  v8::String::AsciiValue env_value(args[1]);
  // joy, putenv requires strings to be null terminated
  std::string k = *env_key;
  std::string v = *env_value;
  std::string s = k + "=" + v;
  char* c = new char[ s.size() + 1 ];
  strcpy(c, s.c_str() );
  c[ s.size() + 1 ] = '\0'; 
  putenv(c);
  return v8::Undefined(); 
};

/* -- Name: ExternalGetEnv(Key,Value)
   -- Description: Gets Key's Value from the System Environment
*/
v8::Handle<v8::Value> ExternalGetEnv(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  v8::String::AsciiValue key(args[0]);
  char* result = getenv(*key);
  if (NULL != result) {
    v8::Handle<v8::String> kval = v8::String::New( result );
    return kval;
  } else {
    return v8::Undefined();
  }
};

/* -- Name: ExternalSystemCall(Cmd)
   -- Description: Wraps InternalSystemCall(); opens a pipe and executes the command.
*/
v8::Handle<v8::Value> ExternalSystemCall(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  v8::Handle<v8::String> Result;
  v8::String::AsciiValue cmd(args[0]);
  std::string Cmd;
  std::string result;
  if ( NULL != *cmd ) Cmd = *cmd;
  if ( Cmd.size() > 0 ) result = InternalSystemCall(Cmd);
  if ( result.size() > 0 ) {
    return v8::String::New( result.c_str() );
  } else { 
    return v8::Undefined();
  }
};

/* -- Name: ExternalStringFileWrite(file,data)
   -- Description: Opens a file and writes data into it
*/
v8::Handle<v8::Value> ExternalStringFileWrite(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  v8::String::AsciiValue filename(args[0]);
  v8::String::AsciiValue data(args[1]);
  WriteFile(*filename, *data);  
  return v8::Undefined();
};

/* -- Name: ExternalStringFileRead(file)
   -- Description: Opens a file and returns its contents
*/
v8::Handle<v8::Value> ExternalStringFileRead(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  v8::String::AsciiValue filename(args[0]);
  char* data = ReadFile(*filename);
  if ( NULL != data ) {
    return v8::String::New(data);
  } else {
    return v8::Undefined();
  }
};

/* -- Name: ExternalExit(return)
   -- Description: Exits with a return value
*/
v8::Handle<v8::Value> ExternalExit(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  int exit_code = args[0]->Int32Value();
  exit(exit_code);
  return v8::Undefined();
};

/* -- Name: ExternalStandardOut(Args[])
   -- Description: Writes Args[] to the STDOUT filehandle
*/
v8::Handle<v8::Value> ExternalStandardOut(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  bool first = true;
  for (int i = 0; i < args.Length(); i++) {
    v8::HandleScope handle_scope;
    if (first) {
      first = false;
    } else {
      printf(" ");
    }
    v8::String::AsciiValue str(args[i]);
    printf("%s", *str);
  }
  return v8::Undefined();
};

/* -- Name: ExternalStandardError(Args[])
   -- Description: Writes Args[] to the STDERR filehandle
*/
v8::Handle<v8::Value> ExternalStandardError(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  bool first = true;
  for (int i = 0; i < args.Length(); i++) {
    v8::HandleScope handle_scope;
    if (first) {
      first = false;
    } else {
      printf(" ");
    }
    v8::String::AsciiValue str(args[i]);
    fprintf(stderr, "%s", *str);
  }
  return v8::Undefined();
};

/* -- Name: ExternalStandardOut(Args[])
   -- Description: Reads a line from the STDIN filehandle
*/
v8::Handle<v8::Value> ExternalStandardIn (const v8::Arguments&args) {
  std::string in = "";
  getline(std::cin, in);
  return v8::String::New( in.c_str() );
};

/* -- Name: ExternalArgc()
   -- Description: This is global argument count method used to share the 'argc' main() value
*/
v8::Handle<v8::Value> ExternalGetArgc(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  return v8::Integer::New(externalArgc);
};

/* -- Name: externalArgv, ExternalGetArgv()
   -- Description: This is global argument array method used to share the 'argv[]' main() value
*/
v8::Handle<v8::Value> ExternalGetArgv(const v8::Arguments& args) {
  v8::Context::Scope context_scope(gblContext);  
  v8::HandleScope handle_scope;
  return externalArgv;
};
